﻿/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#ifndef SHARED_STORAGE_SQLSTORAGEIMPL_H_
#define SHARED_STORAGE_SQLSTORAGEIMPL_H_

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif

#include "Shared/Storage/SQLStorage.h"
#include <NodeCpp/AutoPtr.h>
#include <NodeCpp/Data/Rel/Field.h>
#include <NodeCpp/Data/Rel/QueryResult.h>
using NodeCpp::Data::Rel::Field;
using NodeCpp::Data::Rel::QueryResult;
typedef NodeCpp::AutoPtr<QueryResult>   QueryResultPtr;

template<class DerivedLoader, class StorageClass>
template<class S, class D>
void SQLStorageLoaderBase<DerivedLoader, StorageClass>::convert(std::uint32_t /*field_pos*/, S _Src, D& _Dst)
{
    _Dst = D(_Src);
}

template<class DerivedLoader, class StorageClass>
void SQLStorageLoaderBase<DerivedLoader, StorageClass>::convert_str_to_str(std::uint32_t /*field_pos*/, char const* _Src, char*& _Dst)
{
    if (!_Src)
    {
        _Dst = new char[1];
        *_Dst = 0;
    }
    else
    {
        std::uint32_t _L = strlen(_Src) + 1;
        _Dst = new char[_L];
        memcpy(_Dst, _Src, _L);
    }
}

template<class DerivedLoader, class StorageClass>
template<class S>
void SQLStorageLoaderBase<DerivedLoader, StorageClass>::convert_to_str(std::uint32_t /*field_pos*/, S /*_Src*/, char*& _Dst)
{
    _Dst = new char[1];
    *_Dst = 0;
}

template<class DerivedLoader, class StorageClass>
template<class D>
void SQLStorageLoaderBase<DerivedLoader, StorageClass>::convert_from_str(std::uint32_t /*field_pos*/, char const* /*_Src*/, D& _Dst)
{
    _Dst = 0;
}

template<class DerivedLoader, class StorageClass>
template<class S, class D>
void SQLStorageLoaderBase<DerivedLoader, StorageClass>::default_fill(std::uint32_t /*field_pos*/, S _Src, D& _Dst)
{
    _Dst = D(_Src);
}

template<class DerivedLoader, class StorageClass>
void SQLStorageLoaderBase<DerivedLoader, StorageClass>::default_fill_to_str(std::uint32_t /*field_pos*/, char const* /*_Src*/, char*& _Dst)
{
    _Dst = new char[1];
    *_Dst = 0;
}

template<class DerivedLoader, class StorageClass>
template<class V>
void SQLStorageLoaderBase<DerivedLoader, StorageClass>::StoreValue(V _Value, StorageClass& _Store, char* _P, std::uint32_t _X, std::uint32_t& _Offset)
{
    DerivedLoader* _Subclass = (static_cast<DerivedLoader*>(this));
    switch (_Store.GetDstFormat(_X))
    {
    case DBC_FF_LOGIC:
        _Subclass->convert(_X, _Value, *((bool*)(&_P[_Offset])));
        _Offset += sizeof(bool);
        break;
    case DBC_FF_BYTE:
        _Subclass->convert(_X, _Value, *((char*)(&_P[_Offset])));
        _Offset += sizeof(char);
        break;
    case DBC_FF_INT:
        _Subclass->convert(_X, _Value, *((std::uint32_t*)(&_P[_Offset])));
        _Offset += sizeof(std::uint32_t);
        break;
    case DBC_FF_FLOAT:
        _Subclass->convert(_X, _Value, *((float*)(&_P[_Offset])));
        _Offset += sizeof(float);
        break;
    case DBC_FF_STRING:
        _Subclass->convert_to_str(_X, _Value, *((char**)(&_P[_Offset])));
        _Offset += sizeof(char*);
        break;
    case DBC_FF_NA:
        _Subclass->default_fill(_X, _Value, *((std::uint32_t*)(&_P[_Offset])));
        _Offset += sizeof(std::uint32_t);
        break;
    case DBC_FF_NA_BYTE:
        _Subclass->default_fill(_X, _Value, *((char*)(&_P[_Offset])));
        _Offset += sizeof(char);
        break;
    case DBC_FF_NA_FLOAT:
        _Subclass->default_fill(_X, _Value, *((float*)(&_P[_Offset])));
        _Offset += sizeof(float);
        break;
    case DBC_FF_IND:
    case DBC_FF_SORT:
        NODECPP_ASSERT(false && "SQL storage does not have sort field types");
        break;
    default:
        NODECPP_ASSERT(false && "unknown format character");
        break;
    }
}


template<class DerivedLoader, class StorageClass>
void SQLStorageLoaderBase<DerivedLoader, StorageClass>::StoreValue(char const* _Value, StorageClass& _Store, char* _P, std::uint32_t _X, std::uint32_t& _Offset)
{
    DerivedLoader* _Subclass = (static_cast<DerivedLoader*>(this));
    switch (_Store.GetDstFormat(_X))
    {
    case DBC_FF_LOGIC:
        _Subclass->convert_from_str(_X, _Value, *((bool*)(&_P[_Offset])));
        _Offset += sizeof(bool);
        break;
    case DBC_FF_BYTE:
        _Subclass->convert_from_str(_X, _Value, *((char*)(&_P[_Offset])));
        _Offset += sizeof(char);
        break;
    case DBC_FF_INT:
        _Subclass->convert_from_str(_X, _Value, *((std::uint32_t*)(&_P[_Offset])));
        _Offset += sizeof(std::uint32_t);
        break;
    case DBC_FF_FLOAT:
        _Subclass->convert_from_str(_X, _Value, *((float*)(&_P[_Offset])));
        _Offset += sizeof(float);
        break;
    case DBC_FF_STRING:
        _Subclass->convert_str_to_str(_X, _Value, *((char**)(&_P[_Offset])));
        _Offset += sizeof(char*);
        break;
    case DBC_FF_NA_POINTER:
        _Subclass->default_fill_to_str(_X, _Value, *((char**)(&_P[_Offset])));
        _Offset += sizeof(char*);
        break;
    case DBC_FF_IND:
    case DBC_FF_SORT:
        NODECPP_ASSERT(false && "SQL storage does not have sort field types");
        break;
    default:
        NODECPP_ASSERT(false && "unknown format character");
        break;
    }
}

///////////////////////////////////////////////////////////////////////////
// Load
template<class DerivedLoader, class StorageClass>
template<typename BarType>
bool SQLStorageLoaderBase<DerivedLoader, StorageClass>::Load(Session &_Sess, StorageClass& _Storage, bool _ErrorAtEmpty /* = true */, void(*_ErrorHandler)(const char*, ...) /* = NULL */)
{
    Field* _Fields = NULL;
    // 查询最大编号（Entry）
    QueryResultPtr _Result(_Sess.PQuery("SELECT MAX(%s) FROM %s", _Storage.EntryFieldName(), _Storage.GetTableName()));
    if (!_Result.get()) {
        _ErrorHandler("Error loading %s table (not exist?)\n", _Storage.GetTableName());
        return false;
    }

    std::uint32_t _MaxRecordId = (*_Result)[0].AsUInt32() + 1;
    std::uint32_t _RecordCount = 0;
    std::uint32_t _Recordsize = 0;

    // 查询数据数量（行）
    _Result.reset(_Sess.PQuery("SELECT COUNT(*) FROM %s", _Storage.GetTableName()));
    if (_Result.get())
    {
        _Fields = _Result->Fetch();
        _RecordCount = _Fields[0].AsUInt32();
    }

    // 查询所有数据
    _Result.reset(_Sess.PQuery("SELECT * FROM %s", _Storage.GetTableName()));

    if (!_Result.get())
    {
        _ErrorHandler("%s table is empty!\n", _Storage.GetTableName());
        _RecordCount = 0;
        return _ErrorAtEmpty;
    }

    // 校验数据域数量是否一致
    if (_Storage.GetSrcFieldCount() != _Result->GetFieldCount())
    {
        _RecordCount = 0;
        _ErrorHandler("Error in %s table.Perhaps the table structure was changed."
            " There should be %d fields in the table.\n", 
            _Storage.GetTableName(), _Storage.GetSrcFieldCount());
        return false;
    }

    // 计算目标记录大小
    std::uint32_t _Offset = 0;
    for (std::uint32_t _X = 0; _X < _Storage.GetDstFieldCount(); ++_X)
    {
        switch (_Storage.GetDstFormat(_X))
        {
        case DBC_FF_LOGIC:
            _Recordsize += sizeof(bool);   break;
        case DBC_FF_BYTE:
            _Recordsize += sizeof(char);   break;
        case DBC_FF_INT:
            _Recordsize += sizeof(std::uint32_t); break;
        case DBC_FF_FLOAT:
            _Recordsize += sizeof(float);  break;
        case DBC_FF_STRING:
            _Recordsize += sizeof(char*);  break;
        case DBC_FF_NA:
            _Recordsize += sizeof(std::uint32_t); break;
        case DBC_FF_NA_BYTE:
            _Recordsize += sizeof(char);   break;
        case DBC_FF_NA_FLOAT:
            _Recordsize += sizeof(float);  break;
        case DBC_FF_NA_POINTER:
            _Recordsize += sizeof(char*);  break;
        case DBC_FF_IND:
        case DBC_FF_SORT:
            NODECPP_ASSERT(false && "SQL storage not have sort field types");
            break;
        default:
            NODECPP_ASSERT(false && "unknown format character");
            break;
        }
    }

    // 申请数据存储空间
    _Storage.PrepareToLoad(_MaxRecordId, _RecordCount, _Recordsize);

    BarType _Bar(_RecordCount);
    do
    {
        _Fields = _Result->Fetch();
        _Bar.Step();

        char* _Record = _Storage.CreateRecord(_Fields[0].AsUInt32());
        _Offset = 0;

        // dependend on dest-size
        // iterate two indexes: _X over dest, _Y over source
        //                      _Y++ If and only If _X != FT_NA*
        //                      _X++ If and only If a value is stored
        for (std::uint32_t _X = 0, _Y = 0; _X < _Storage.GetDstFieldCount();)
        {
            switch (_Storage.GetDstFormat(_X))
            {
                // For default fill continue and do not increase _Y
            case DBC_FF_NA:         StoreValue((std::uint32_t)0, _Storage, _Record, _X, _Offset);         ++_X; continue;
            case DBC_FF_NA_BYTE:    StoreValue((char)0, _Storage, _Record, _X, _Offset);           ++_X; continue;
            case DBC_FF_NA_FLOAT:   StoreValue((float)0.0f, _Storage, _Record, _X, _Offset);       ++_X; continue;
            case DBC_FF_NA_POINTER: StoreValue((char const*)NULL, _Storage, _Record, _X, _Offset); ++_X; continue;
            default:
                break;
            }

            // It is required that the input has at least as many columns set as the output requires
            if (_Y >= _Storage.GetSrcFieldCount()) {
                NODECPP_ASSERT(false && "SQL storage has too few columns!");
            }

            switch (_Storage.GetSrcFormat(_Y))
            {
            case DBC_FF_LOGIC:  StoreValue((bool)(_Fields[_Y].AsUInt32() > 0), _Storage, _Record, _X, _Offset);  ++_X; break;
            case DBC_FF_BYTE:   StoreValue((char)_Fields[_Y].AsUInt8(), _Storage, _Record, _X, _Offset);         ++_X; break;
            case DBC_FF_INT:    StoreValue((std::uint32_t)_Fields[_Y].AsUInt32(), _Storage, _Record, _X, _Offset);      ++_X; break;
            case DBC_FF_FLOAT:  StoreValue((float)_Fields[_Y].AsFloat(), _Storage, _Record, _X, _Offset);        ++_X; break;
            case DBC_FF_STRING: StoreValue((char const*)_Fields[_Y].c_str(), _Storage, _Record, _X, _Offset); ++_X; break;
            case DBC_FF_NA:
            case DBC_FF_NA_BYTE:
            case DBC_FF_NA_FLOAT:
                // Do Not increase _X
                break;
            case DBC_FF_IND:
            case DBC_FF_SORT:
            case DBC_FF_NA_POINTER:
                NODECPP_ASSERT(false && "SQL storage not have sort or pointer field types");
                break;
            default:
                NODECPP_ASSERT(false && "unknown format character");
            }
            ++_Y;
        }
    } while (_Result->NextRow());

    return true;
}

#endif /* SHARED_STORAGE_SQLSTORAGEIMPL_H_ */
